package com.sinosoft.modules.code;

import java.io.File;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.nutz.dao.Cnd;
import org.nutz.dao.Condition;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.json.Json;
import org.nutz.lang.Files;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.By;
import org.nutz.mvc.annotation.Filters;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;

import com.sinosoft.common.action.BaseAction;
import com.sinosoft.common.datatype.DateTime;
import com.sinosoft.common.util.CamelCaseUtils;
import com.sinosoft.common.util.FileUtil;
import com.sinosoft.common.util.ReadWriteFileWithEncode;
import com.sinosoft.common.util.SortList;
import com.sinosoft.common.util.StringUtil;
import com.sinosoft.common.util.ZipUtil;
import com.sinosoft.common.web.filter.BaseFilter;
import com.sinosoft.common.web.filter.UserLoginFilter;
import com.sinosoft.domain.base.BaseFile;
import com.sinosoft.domain.code.CodeDbConfig;
import com.sinosoft.domain.code.CodeDbMapping;
import com.sinosoft.domain.code.CodeMapType;
import com.sinosoft.domain.code.CodePlan;
import com.sinosoft.domain.code.CodePlanItem;
import com.sinosoft.plugin.code.NutzDaoUtil;
import com.sinosoft.plugin.code.pdm.DBParser;
import com.sinosoft.plugin.code.pdm.PDM;
import com.sinosoft.plugin.code.pdm.PDMColumn;
import com.sinosoft.plugin.code.pdm.PDMKey;
import com.sinosoft.plugin.code.pdm.PDMTable;
import com.sinosoft.plugin.code.pdm.Parser;
import com.sinosoft.plugin.freemarker.FreemarkerUtil;

/**
 * @author yangqunwei
 * @mail yqw8912@163.com
 * @time  2015-05-20 16:57:31 
 * 代码生成
 */
@IocBean
@At("/code/CodeGen")
@Filters({@By(type = UserLoginFilter.class), @By(type = BaseFilter.class)})
public class CodeGenAction extends BaseAction {	
	private static final String target_system ="system";
	private static final String target_table ="table";
	private static final String target_column ="column";
	/**
	 * 代码生成
	 * @param req
	 * @param response
	 */
	@At("")
	public void codeGenerate(HttpServletRequest req,HttpServletResponse response,
			@Param("planId") String planId,
			@Param("genType") String genType,
			@Param("pdmFileId") String pdmFileId,
			@Param("dbConFig") String dbConfigId,
			@Param("encoding") String encoding,
			@Param("table_key") String table_key,
			@Param("tablePrefix") String tablePrefix,
			@Param("selectFile") String[] selectFileArray,
			@Param("dbMapList") String[] dbMapList,
			@Param("table_id")String[] tableList) {
		String errorMesage = null;
		String zipId = null;
		try {
			while(true){
				if(null==tableList||tableList.length==0){
					errorMesage ="请选择至少一个表生成代码!";
					break;
				}	
				if(null==selectFileArray||selectFileArray.length==0){
					errorMesage ="请选择至少一个文件类型生成代码!";
					break;
				}	
				PDM pdm = null;				
				if("PDM".equals(genType)){
					if(null==pdmFileId||"".equals(pdmFileId)){
						errorMesage ="使用PDM生成时必须保证PDM文件已经上传成功!";
						break;
					}else{
						BaseFile pdmFile = daoUtil.detailByPK(BaseFile.class, pdmFileId);
						pdm = new Parser().pdmParser(pdmFile.getRealFilePath());
					}
				}				
				if("DB".equals(genType)){
					if(null==dbConfigId||"".equals(dbConfigId)){
						errorMesage ="请选择一个有效的数据链接!";
						break;
					}
					if(null==table_key||"".equals(table_key)){
						errorMesage ="必须输入表关键信息!";
						break;
					}		
					CodeDbConfig config = daoUtil.detailByPK(CodeDbConfig.class,dbConfigId);
					pdm = new DBParser().pdmParser(config,table_key);		
				}
				if(null==pdm){				
					errorMesage ="获取表信息失败,系统异常!";
					break;
				}
				if(null==dbMapList||dbMapList.length==0){				
					errorMesage ="没有选择任何数据映射!";
					break;
				}				
				if(null==pdm.getTables()||pdm.getTables().size()<=0){				
					errorMesage ="获取表信息失败,系统异常!";
					break;
				}				
				//需要传送给FreeMarker的参数
				Map<String,Object> inputMap = new HashMap<String,Object>();
				
				//查询所有需要处理的模版
				Condition cnd =Cnd.where("planItemId","in",selectFileArray).and("planId", "=", planId);
				List<CodePlanItem> tempList = daoUtil.getDao().query(CodePlanItem.class, cnd);
				
				
				//查询所有的映射并进行转换
				List<CodeMapType> mapTypeArray = null;
				mapTypeArray = daoUtil.getDao().query(CodeMapType.class, Cnd.where("mapTypeId","in",dbMapList));
				Map<String,List<CodeDbMapping>> dbTypeMap = new HashMap<String,List<CodeDbMapping>>();
				for(int i=0;i<mapTypeArray.size();i++){
					CodeMapType mapType = mapTypeArray.get(i);
					String mapTarget = mapType.getMapTarget();
					CodeDbMapping codeDbMapping = new CodeDbMapping();
					codeDbMapping.setMapTypeId(mapType.getMapTypeId());
					List<CodeDbMapping> codeDbMapList = daoUtil.queryList(codeDbMapping);	
					if(null==codeDbMapList||codeDbMapList.size()<=0){
						continue;						
					}
					for(int k=0;k<codeDbMapList.size();k++){
						codeDbMapList.get(k).setMapName(mapType.getMapName());
					}
					if(dbTypeMap.containsKey(mapTarget)){
						dbTypeMap.get(mapTarget).addAll(codeDbMapList);
					}else{
						dbTypeMap.put(mapTarget, codeDbMapList);
					}
				}				
				//开始拼装参数
				//1-处理系统级别的参数
				Map<String,Object> systemMap = new HashMap<String,Object>();
				systemMap.put("A_CurrentTime", DateTime.current().toString());
				systemMap.putAll(getExtMap(dbTypeMap.get(target_system),inputMap));
				inputMap.putAll(systemMap);
				//开始遍历表信息
				List<PDMTable> tables = pdm.getTables();
				List<Map<String,Object>> tableListInput = new ArrayList<Map<String,Object>>();
				inputMap.put("A_TableList", tableListInput);
				//
				Map<String,String> selectTableMap = new HashMap<String,String>();
				for(int i=0;i<tableList.length;i++){
					selectTableMap.put(tableList[i], "");
				}
				for(int i=0;i<tables.size();i++){
					PDMTable table = tables.get(i);
					if(selectTableMap.containsKey(table.getId())){
						Map<String,Object> tableMap = new HashMap<String,Object>();
						tableMap.put("T_Code", table.getCode());
						tableMap.put("T_Name", table.getName());
						tableMap.putAll(getExtMap(dbTypeMap.get(target_table),systemMap,tableMap));
						//便利属性信息
						List<Map<String,Object>> columnListInput = new ArrayList<Map<String,Object>>();
						tableMap.put("T_ColumnList", columnListInput);
						List<PDMColumn> columns = table.getColumns();
						for(int k=0;k<columns.size();k++){
							PDMColumn column = columns.get(k);
							Map<String,Object> columnMap = new HashMap<String,Object>();
							columnMap.put("C_Code", column.getCode());
							columnMap.put("C_Name", column.getName());
							columnMap.put("C_Comment", column.getComment());
							columnMap.put("C_DataType", column.getDataType());
							columnMap.put("C_Length", column.getLength());
							columnMap.put("C_Mandatory", column.getMandatory());
							columnMap.put("C_Precision", column.getPrecision());
							columnMap.put("C_DataTypeExt", column.getDataTypeExt());
							columnMap.putAll(getExtMap(dbTypeMap.get(target_column),systemMap,tableMap,columnMap));
							columnListInput.add(columnMap);
						}
						//便利主键信息
						List<Map<String,Object>> keyListInput = new ArrayList<Map<String,Object>>();
						tableMap.put("T_KeyList", keyListInput);		
						List<PDMKey> keys =  table.getKeys();
						if(null!=keys&&keys.size()>0){
							List<PDMColumn> keyColumns =	keys.get(0).getColumns();
							for(int k=0;k<keyColumns.size();k++){
								PDMColumn column = keyColumns.get(k);
								Map<String,Object> columnMap = new HashMap<String,Object>();
								columnMap.put("C_Code", column.getCode());
								columnMap.put("C_Name", column.getName());
								columnMap.put("C_Comment", column.getComment());
								columnMap.put("C_DataType", column.getDataType());
								columnMap.put("C_Length", column.getLength());
								columnMap.put("C_Mandatory", column.getMandatory());
								columnMap.put("C_Precision", column.getPrecision());
								columnMap.put("C_DataTypeExt", column.getDataTypeExt());
								columnMap.putAll(getExtMap(dbTypeMap.get(target_column),systemMap,tableMap,columnMap));
								keyListInput.add(columnMap);
							}							
						}
						tableListInput.add(tableMap);
					}
				}
				//设定临时文件目录
				String temePath = DateTime.current().toString(DateTime.YEAR_TO_SECOND).replaceAll(" ","").replaceAll(":", "").replaceAll("-", "");
				String basePacket = planId+"."+temePath+".";	
//				System.out.println(Json.toJson(inputMap));
				//变量处理结束,开始生成文件
				for(int i=0;i<tempList.size();i++){
					CodePlanItem codeTem = tempList.get(i);
					if(target_system.equals(codeTem.getMapTarget())){
						createFile(codeTem, inputMap, basePacket, encoding);
					}else if(target_table.equals(codeTem.getMapTarget())){						
						List<Map<String,Object>> tableListTemp = (List<Map<String, Object>>) inputMap.get("A_TableList");
						for(int t=0;t<tableListTemp.size();t++){							
							Map<String,Object> tableMap = new HashMap<String,Object>();							
							tableMap.putAll(systemMap);					
							tableMap.putAll(tableListTemp.get(t));
							createFile(codeTem, tableMap, basePacket, encoding);
						}
					}
				}
				CodePlan codePlan = daoUtil.detailByPK(CodePlan.class, planId);
				String realCodePath = FileUtil.getFilePath(basePacket);
	        	DateTime current = DateTime.current();
	        	String filePath = daoUtil.getUserID()+".zip."+current.getYear()+"."+current.getMonth()+"."+current.getDay()+".";
	        	BaseFile baseFile = new BaseFile();
	        	baseFile.setFileExt("zip");
	        	baseFile.setFileName(codePlan.getPlanName());
	        	baseFile.setFileSize(0.00);//因为是系统生成的暂不计入总使用容量
	        	baseFile.setFilePath(filePath);
	        	baseFile.setValid("1");
	        	daoUtil.add(baseFile);    
	        	zipId = baseFile.getFileId();
				ZipUtil.zip(new File(baseFile.getRealFilePath()), realCodePath);
				break;
			}		
		} catch (Exception e) {
			e.printStackTrace();
			ajaxDoneError(response,e);		
		}	
		if(null==errorMesage){
			ajaxDoneSuccess(response,successMessage+"<script type=\"text/javascript\">"+"downFile(\""+zipId+" \");"+"</script>",false);
			ajaxDoneSuccess(response,successMessage,false);		
		}else{
			ajaxDoneError(response,errorMesage);
		}
	}
	/**
	 * 根据条件及入参生成文件条件
	 * @param codeTem
	 * @param inputMap
	 * @param basePacket
	 * @param encoding
	 */
	private void createFile(CodePlanItem codeTem,Map<String,Object> inputMap,String basePacket,String encoding){
		String fileExt = FreemarkerUtil.generateByFtlTemplate(codeTem.getFileExt(), inputMap);
		String fileName = FreemarkerUtil.generateByFtlTemplate(codeTem.getFileName(), inputMap);
		String filePath = FreemarkerUtil.generateByFtlTemplate(codeTem.getFilePath(), inputMap);
		String fileConText = FreemarkerUtil.generateByFtlTemplate(codeTem.getFileContext(), inputMap);
		writeFile(basePacket, filePath, fileName, fileConText, fileExt, encoding);
	}
	
	/**
	 * 根据PDM中的值以及条件组装扩展信息
	 * @param list
	 * @param inputObjMap
	 * @return
	 */
	private Map<String,Object> getExtMap(
			List<CodeDbMapping> list, Map<String,Object>... inputObjMapList) {
		Map<String,Object> extMap = new HashMap<String,Object>();
		if(null==list||list.size()<=0){
			return extMap;
		}
		SortList.Sort(list,"coverLevel");
//		System.out.println(Json.toJson(list));
		for(int i=0;i<list.size();i++){
			CodeDbMapping codeMap = list.get(i);
			String key = codeMap.getMapName();
			if(null!=codeMap.getCoverName()&&!"".equals(codeMap.getCoverName())){
				key = key+"_"+codeMap.getCoverName();
			}
			String outVal = "";
			String inputVal = getRealVal(codeMap.getCoverIn(),extMap,inputObjMapList);
			String coverVal =getRealVal(codeMap.getCoverVal(),extMap,inputObjMapList);		
			String coverOut =getRealVal(codeMap.getCoverOut(),extMap,inputObjMapList);	 
			String coverType = codeMap.getCoverType();
			if("Static".equals(coverType)){//静态变量
				outVal = coverOut;
			}else if("X2TU".equals(coverType)){				
				outVal = CamelCaseUtils.toCapitalizeCamelCase(inputVal);
			}else if("T2X".equals(coverType)){
				outVal = CamelCaseUtils.toUnderlineName(inputVal);
			}else if("X2TL".equals(coverType)){
				outVal = CamelCaseUtils.toCamelCase(inputVal);
			}else if("EQU".equals(coverType)){
				if(null!=inputVal&&!"".equals(inputVal)&&null!=coverVal&&!"".equals(coverVal)&&inputVal.equals(coverVal)){
					outVal = coverOut;
				}
			}else if("REPLACE".equals(coverType)){
				outVal =inputVal.replaceAll(coverVal, coverOut);
			}else if("Upper".equals(coverType)){
				outVal = inputVal.toUpperCase();
			}else if("Lower".equals(coverType)){
				outVal = inputVal.toLowerCase();
			}else if("NotNull".equals(coverType)){
				if(null!=inputVal&&!"".equals(inputVal)){
					outVal = coverOut;
				}
			}
			if(null!=outVal&&!"".equals(outVal)){
				extMap.put(key, outVal);
			}
		}
		return extMap;
	}
	private String getRealVal(String realObj,Map<String,Object> extMap,Map<String,Object>... inputObjMapList){
		String realVal = "";
		if(null!=realObj){
			for(int i=0;i<inputObjMapList.length;i++){
				Map<String,Object> inputObjMap = inputObjMapList[i];
				String tempVal = FreemarkerUtil.generateByFtlTemplate(realObj, new HashMap());
				realVal = FreemarkerUtil.generateByFtlTemplate(realObj,inputObjMap);
				if(null!=realVal&&null!=tempVal&&!realVal.equals(tempVal)){
					return realVal;
				}				
			}
			return FreemarkerUtil.generateByFtlTemplate(realObj,extMap);
		}	
		return realVal;
	}
	/**
	 * 根据条件写入文件
	 * @param basePacket 生成文件的跟目录
	 * @param filePath 可以是分隔符的也可以是直接地址
	 * @param fileExt
	 * @param fileName
	 * @param fileConText
	 * @param encoding
	 */
	private static  void writeFile(String basePacket,String filePath,String fileName,String fileConText,String fileExt,String encoding){
		try {			
			String totalPacket = basePacket+"."+filePath;
			String filePathReal =FileUtil.getFilePath(totalPacket) +fileName;
			if(null!=fileExt&&!"".equals(fileExt)){
				filePathReal = filePathReal+"."+fileExt;
			}
			File file = new File(filePathReal);
			FileUtil.createNewFile(file);	
			ReadWriteFileWithEncode.write(file, fileConText,encoding);
		} catch (Exception e) {
			e.printStackTrace();
		}		
	}	
	/**
	 * 代码生成
	 * @param req
	 * @param response
	 * @throws Exception 
	 */
	@At	
	@Ok("jsp:jsp.code.listCodeTable")
	public void getTable(HttpServletRequest req, HttpServletResponse response,@Param("pdmFileId") String pdmFileId,@Param("table_key") String table_key,@Param("dbConFig") String dbConFig) {
		if(null!=pdmFileId&&!"".equals(pdmFileId)){
			try {
				BaseFile pdmFile = daoUtil.detailByPK(BaseFile.class, pdmFileId);
				PDM pdm = new Parser().pdmParser(pdmFile.getRealFilePath());
				req.setAttribute("pdm", pdm);
			} catch (Exception e) {
				ajaxDoneError(response, "PDM解析失败!<br/>请检查PDM中是否有未设置的属性类型!");
			}
		}else if(null!=dbConFig&&!"".equals(dbConFig)){
			try {
				CodeDbConfig config = daoUtil.detailByPK(CodeDbConfig.class,dbConFig);
				PDM pdm = new DBParser().pdmParser(config,table_key);
				req.setAttribute("pdm", pdm);
			} catch (Exception e) {
				e.printStackTrace();
				ajaxDoneError(response,e);		
			}				
		}
//		ajaxDoneError(response, "没有正确的选择数据方式!");
	}
}